package net.lastcoder.ifserver.nio;

import net.lastcoder.ifserver.Constants;
import net.lastcoder.ifserver.Globals;
import net.lastcoder.ifserver.packet.Event;
import net.lastcoder.ifserver.packet.Message;
import net.lastcoder.ifserver.packet.Packet;
import net.lastcoder.ifserver.packet.PacketParser;
import net.lastcoder.ifserver.packet.SessionClosePacket;
import net.lastcoder.ifserver.packet.SessionOpenPacket;
import net.lastcoder.ifserver.packet.ZeroPacket;
import net.lastcoder.ifserver.session.UlaID;
import net.lastcoder.ifserver.util.CRC32;
import net.lastcoder.ifserver.util.DataTypeUtil;
import net.lastcoder.ifserver.util.QuickCrypter;

import org.apache.mina.core.buffer.IoBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;

/**轻量级的包解析，缓存大小为128K，请熟读文档后在来修改
 * auth: WenYF
 * date: 2016/10/8
 */
public class LightPacketParser extends PacketParser {
    private static final Logger Log = LoggerFactory.getLogger(LightPacketParser.class);
    //private static final Logger Log = new MyLogger();

    private static final short HEAD = (short) 0xAADD;
    private static final byte HEAD_LOW = (byte) 0xDD;
    private static final byte HEAD_HIGH = (byte) 0xAA;

    private static final String MAX_PROPERTY_NAME = "parser.buffer.size";

    private int maxBufferSize;

    /**
     * 将数据放在这里面
     */
    private IoBuffer byteBuffer;

    private boolean findHead;
    private ByteBuffer headCollector;
    private ByteBuffer dataSizeCollector;
    private int dataSize;
    private int readPosition;

    private ArrayList<Packet> packets;

    public LightPacketParser() {
        // 32kb
        maxBufferSize = Globals.getXMLProperty(MAX_PROPERTY_NAME, 1024 * 32);
        byteBuffer = IoBuffer.allocate(maxBufferSize);
        byteBuffer.setAutoExpand(true);
        byteBuffer.setAutoShrink(true);

        headCollector = ByteBuffer.allocate(2);
        dataSizeCollector = ByteBuffer.allocate(4);
        dataSizeCollector.limit(dataSizeCollector.capacity());
        packets = new ArrayList<>();

        reset();
    }

    private void reset() {
        dataSizeCollector.clear();
        headCollector.clear();
        byteBuffer.clear();
        findHead = false;
        dataSize = -1;
        readPosition = 0;
    }

    /**
     * @param buffer 必须有数据可读取，必须单线程调用
     */
    @Override
    public synchronized void read(IoBuffer buffer) {
        if (ENABLE_PARSER_DEBUG) {
            buffer.mark();
            Thread thread = Thread.currentThread();
            String dump = buffer.getHexDump();
            Log.debug("thread name = " + thread.getName());
            Log.debug("buffer : " + dump);
            buffer.reset();
        }
        // 采用循环方式
        while (buffer.hasRemaining()) {
            // 把在通道中的数据全部读取出来
            // 采用边都去边解析
            if (ENABLE_PARSER_DEBUG) {
                Log.debug("findHead = " + findHead);
            }
            if (ENABLE_PARSER_DEBUG) {
                Log.debug("dataSize = " + dataSize);
            }

            // 没有找到头，就去找头， 直到找到为止
            if (!findHead) {
                // 本地buffer中没有数据
                boolean parserOk = parserHead(buffer);
                if (ENABLE_PARSER_DEBUG) {
                    Log.debug("parser head Ok = " + parserOk);
                }

                if (!parserOk) {
                    continue;
                }
            }

            if (findHead && dataSize < 0) {
                // 读取长度
                boolean parserOk = parserDataSize(buffer);
                if (ENABLE_PARSER_DEBUG) {
                    Log.debug("parser data size Ok = " + parserOk);
                }

                if (!parserOk) {
                    continue;
                }
            }

            if (findHead && dataSize == 0) {
                reset();
            }

            if (findHead && dataSize > 0) {
//                if (dataSize > byteBuffer.capacity() - byteBuffer.position()) {
//                    reset();
//                    return;
//                }

                // 如果当前正在已经在解析包，那么读完所有数据后，才进行下一次读取
                int size = buffer.limit() - buffer.position();
                if (ENABLE_PARSER_DEBUG) {
                    Log.debug("buffer remain size = " + size);
                    Log.debug("dataSize = " + dataSize + ", readPosition = " + readPosition);
                }
                // done 如果为true表示本次数据足够解析成一个包
                boolean done = false;
                if (size >= dataSize - readPosition) {
                    size = dataSize - readPosition;
                    done = true;
                }
                for (int i = 0; i < size; i++) {
                    byteBuffer.put(buffer.get());
                    readPosition++;
                }

                // 要么读取完成，要么返回
                if (done) {
                    // 这里一条读取完毕
                    // 处理数据
                    parserData();
                }
            }

        }

        //以下是递归方式
//        if (!buffer.hasRemaining()) {
//            return;
//        }
//        // 把在通道中的数据全部读取出来
//        // 采用边都去边解析
//        if (ENABLE_PARSER_DEBUG) {
//            Log.debug("findHead = " + findHead);
//        }
//        if (findHead) {
//            // 读取长度
//            if (ENABLE_PARSER_DEBUG) {
//                Log.debug("dataSize = " + dataSize);
//            }
//            if (dataSize == -1) {
//                boolean parserOk = parserDataSize(buffer);
//                if (ENABLE_PARSER_DEBUG) {
//                    Log.debug("parser data size Ok = " + parserOk);
//                }
//                if (!parserOk) {
//                    return;
//                } else {
//                    read(buffer);
//                }
//            } else {
//                // do nothing
//            }
//        } else {
//            // 本地buffer中没有数据
//            boolean parserOk = parserHead(buffer);
//            if (ENABLE_PARSER_DEBUG) {
//                Log.debug("parser head Ok = " + parserOk);
//            }
//            if (!parserOk) {
//                return;
//            } else {
//                read(buffer);
//            }
//        }
//
//        // 数据长度为0
//        if (dataSize == 0) {
//            reset();
//            return;
//        }
//
//        if (!buffer.hasRemaining()) {
//            return;
//        }
//
//        // 如果当前正在已经在解析包，那么读完所有数据后，才进行下一次读取
//        int size = buffer.limit() - buffer.position();
//        if (ENABLE_PARSER_DEBUG) {
//            Log.debug("buffer remain size = " + size);
//            Log.debug("dataSize = " + dataSize + ", readPosition = " + readPosition);
//        }
//        // done 如果为true表示本次数据足够解析成一个包
//        boolean done = false;
//        if (findHead && dataSize > 0) {
//            if (size >= dataSize - readPosition) {
//                size = dataSize - readPosition;
//                done = true;
//            }
//            for (int i = 0; i < size; i++) {
//                byteBuffer.put(buffer.get());
//                readPosition++;
//            }
//        }
//
//        // 要么读取完成，要么返回
//        if (done) {
//            // 这里一条读取完毕
//            // 处理数据
//            parserData();
//            read(buffer);
//        } else {
//            // buffer中的全部数据都不能填满所需要的
//            // 读取完了就返回
//            return;
//        }
    }

    private boolean parserHead(IoBuffer buffer) {
        boolean one = false;
        if (buffer.remaining() == 1) {
            one = true;
        }
        while (buffer.hasRemaining()) {
            byte v = buffer.get();
            if (v == 0 && one && headCollector.position() == 0) {
                ZeroPacket zeroPacket = new ZeroPacket();
                packets.add(zeroPacket);
                if (ENABLE_PARSER_DEBUG) {
                    Log.debug("parser a zero packet");
                }
                break;
            }
            headCollector.put(v);
            // 收集到了2个字节
            //Log.debug("parserDataSize position = " + dataSizeCollector.position());
            if (headCollector.position() == headCollector.capacity()) {
                short headValue = headCollector.getShort(0);
                headCollector.clear();
                if (headValue == HEAD) {
                    findHead = true;
                    return true;
                }
            }
        }

        return false;
    }

    private boolean parserDataSize(IoBuffer buffer) {
        while (buffer.hasRemaining()) {
            dataSizeCollector.put(buffer.get());
            // 收集到了4个字节
            //Log.debug("parserDataSize position = " + dataSizeCollector.position());
            if (dataSizeCollector.position() == dataSizeCollector.capacity()) {
                dataSize = dataSizeCollector.getInt(0);
                dataSizeCollector.clear();
                return true;
            }
        }

        return false;
    }

    private void parserData() {
        byteBuffer.flip();

        CRC32 crc32 = new CRC32();
        crc32.update(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit() - 4);
        int value = (int) crc32.getValue();

        // crc 校验
        byteBuffer.mark();
        byteBuffer.position(byteBuffer.limit() - 4);
        int crc = byteBuffer.getInt();

        if (ENABLE_PARSER_DEBUG) {
            Log.debug("calu crc = " + Arrays.toString(DataTypeUtil.intToByteArray(value))
                    + ", pend crc = " + Arrays.toString(DataTypeUtil.intToByteArray(crc)));
            Log.debug("calu crc = " + value + ", pend crc = " + crc);
        }


        byteBuffer.rewind();
        if (crc != value) {
            Log.warn("A wrong packet, not crc.");
            Log.warn(byteBuffer.getHexDump());
            reset();
            return;
        }


        byteBuffer.position(0);

        // 解密
        QuickCrypter.decrypt(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit() - 4);

        String dump = byteBuffer.getHexDump();
        if (ENABLE_PARSER_DEBUG) {
            Log.debug("data : " + dump);
        }
        byte type = byteBuffer.get();
        int msgId = byteBuffer.getInt();
        if (ENABLE_PARSER_DEBUG) {
            Log.debug("parser msgId = " + msgId);
        }

        short toSize = byteBuffer.getShort();
        if (ENABLE_PARSER_DEBUG) {
            Log.debug("parser toSize = " + toSize);
        }
        byte[] toBytes = new byte[toSize];
        byteBuffer.get(toBytes);
        String to = new String(toBytes);
        if (ENABLE_PARSER_DEBUG) {
            Log.debug("parser to = " + to);
        }

        short fromSize = byteBuffer.getShort();
        if (ENABLE_PARSER_DEBUG) {
            Log.debug("parser fromSize = " + fromSize);
        }
        byte[] fromBytes = new byte[fromSize];
        byteBuffer.get(fromBytes);
        String from = new String(fromBytes);
        if (ENABLE_PARSER_DEBUG) {
            Log.debug("parser from = " + from);
        }

        // 必须在这里完成所有解析工作，因为要后期可能替换
        Packet packet = null;
        switch (type) {
            case Constants.PACKET_TYPE_SESSION_OPEN: {
                int dataSize = byteBuffer.getInt();
                if (ENABLE_PARSER_DEBUG) {
                    Log.debug("parser dataSize = " + dataSize);
                }

                if (dataSize >= 4) {
                    byte sessionType = byteBuffer.get();
                    SessionOpenPacket sessionPacket = new SessionOpenPacket(sessionType);

                    if (sessionType == Packet.TYPE_SET) {
                        byte protocolVersion = byteBuffer.get();
                        sessionPacket.setProtocolVersion(protocolVersion);
                    }

                    dataSize = byteBuffer.getShort();
                    if (ENABLE_PARSER_DEBUG) {
                        Log.debug("parser extra dataSize = " + dataSize);
                    }
                    byte[] extra = new byte[dataSize];


                    byteBuffer.get(extra);
                    sessionPacket.setExtra(extra);

                    packet = sessionPacket;
                }

                break;
            }
            case Constants.PACKET_TYPE_SESSION_CLOSE: {
                int dataSize = byteBuffer.getInt();
                if (ENABLE_PARSER_DEBUG) {
                    Log.debug("parser dataSize = " + dataSize);
                }

                if (dataSize >= 3) {
                    byte sessionType = byteBuffer.get();
                    SessionClosePacket sessionPacket = new SessionClosePacket(sessionType);

                    dataSize = byteBuffer.getShort();
                    if (ENABLE_PARSER_DEBUG) {
                        Log.debug("parser extra dataSize = " + dataSize);
                    }
                    byte[] extra = new byte[dataSize];
                    byteBuffer.get(extra);
                    sessionPacket.setExtra(extra);

                    packet = sessionPacket;
                }
                break;
            }
            case Constants.PACKET_TYPE_EVENT: {
                int dataSize = byteBuffer.getInt();
                if (ENABLE_PARSER_DEBUG) {
                    Log.debug("parser dataSize = " + dataSize);
                }

                if (dataSize >= 7) {
                    short eventType = byteBuffer.getShort();
                    byte queryType = byteBuffer.get();
                    int actionCode = byteBuffer.getInt();
                    Event event = new Event(eventType, queryType, actionCode);

                    if (byteBuffer.remaining() > 4) {
                        dataSize = byteBuffer.getInt();
                    } else {
                        dataSize = 0;
                    }

                    if (ENABLE_PARSER_DEBUG) {
                        Log.debug("parser event dataSize = " + dataSize);
                    }
                    byte[] dataBytes = new byte[dataSize];
                    byteBuffer.get(dataBytes);
                    if (ENABLE_PARSER_DEBUG) {
                        Log.debug("parser dataBytes length = " + dataBytes.length);
                    }

                    event.setBody(dataBytes);

                    packet = event;
                }
                break;
            }
            case Constants.PACKET_TYPE_MESSAGE: {
                int dataSize = byteBuffer.getInt();
                if (ENABLE_PARSER_DEBUG) {
                    Log.debug("parser dataSize = " + dataSize);
                }

                if (dataSize > 5) {
                    dataSize = byteBuffer.get();
                    if (ENABLE_PARSER_DEBUG) {
                        Log.debug("parser title dataSize = " + dataSize);
                    }
                    byte[] title = new byte[dataSize];
                    byteBuffer.get(title);

                    Message message = new Message(title);

                    if (byteBuffer.remaining() > 4) {
                        dataSize = byteBuffer.getInt();
                    } else {
                        dataSize = 0;
                    }

                    if (ENABLE_PARSER_DEBUG) {
                        Log.debug("parser body dataSize = " + dataSize);
                    }
                    byte[] dataBytes = new byte[dataSize];
                    byteBuffer.get(dataBytes);
                    if (ENABLE_PARSER_DEBUG) {
                        Log.debug("parser body length = " + dataBytes.length);
                    }
                    message.setBody(dataBytes);

                    packet = message;
                }
                break;
            }
            default:
                // TODO 没有找到
                break;
        }

        if (packet != null) {
            packet.setFromAddress(UlaID.fromAddress(from));
            packet.setToAddress(UlaID.fromAddress(to));
            packet.setId(msgId);

            packets.add(packet);
        }

        reset();
    }

    @Override
    public boolean hasPacket() {
        return packets.size() > 0;
    }

    @Override
    public Packet getPacket() {
        return packets.remove(0);
    }

}
